BZLog Logging System
BZScript Logger (bzlog.e)
This module provides a simple, extensible logging system for use throughout the BZScript ecosystem. It supports multiple log levels, timestamps, and log file output.
Features
- Custom logger object type using eumem-based struct
- Support for six log levels:
- SILENT
- ERR
- INFO
- DEBUG
- TRACE
- VERBOSE
- Logs include timestamps
- Messages written to a specified log file
- Optional log level filtering
- Safe memory management and type checking
Usage
include lib/utils/bzlog.e
Creating a Logger
TBzLog mylog = bzlog:new(DEBUG, "logfile.log")
This creates a logger that writes to "logfile.log" and includes messages at DEBUG level and above.
Writing Messages
bzlog:write(mylog, INFO, "Program started") bzlog:write(mylog, DEBUG, "Debugging a thing") bzlog:write(mylog, ERR, "Something went wrong!")
Only messages equal to or more severe than the logger's level are written.
Releasing the Logger
bzlog:free(mylog)
Closes the log file and frees memory.
Log Levels
Level | Description |
---|---|
SILENT | No output |
ERR | Errors only |
INFO | Informational events |
DEBUG | Debug output |
TRACE | Verbose tracing |
VERBOSE | Everything |
Output Format
Each log message is timestamped:
[2025-05-20 14:13:23] [debug] Token stream initialized
Notes
- Internally, bzlog uses eumem to emulate an object system
- Each logger validates its type ID before access
- Timestamps are formatted using datetime:now() and format()
-- bzlog.e namespace bzlog include std/eumem.e include std/io.e include std/console.e include std/filesys.e include std/get.e include std/datetime.e -- -- this is our structure (let's hide the implementation details) -- enum __TYPE__, -- must be first value in enum _log_level, _log_file, _log_handle, __MYSIZE__ -- must be last value in enum -- -- ID pattern is SOMETHING_THAT_MAKES_SENSE DOLLAR_SYMBOL SOME_RANDOM_CHARS -- constant BZLOG_ID = "BzLog$TES@#&sdfhsfhsaGHSf%^" -- Create a type checker public type TBzLog (atom me) if eumem:valid(me, __MYSIZE__) then if equal(eumem:ram_space[me][__TYPE__], BZLOG_ID) then return 1 end if end if return 0 end type constant SIZEOF_BZLOG = __MYSIZE__ public enum SILENT, ERR, INFO, DEBUG, TRACE, VERBOSE -- -- create a new object -- public function new( integer log_level, sequence log_file) integer log_handle = -1 if not file_exists(log_file) then log_handle = open(log_file, "w") else log_handle = open(log_file, "a") end if if log_handle = -1 then puts(1, "[bzlog] Failed to open log file.\n") abort(1) end if return eumem:malloc( {BZLOG_ID, log_level, log_file, log_handle, SIZEOF_BZLOG} ) end function -- public API public function free(TBzLog me) close_logger(me) eumem:free(me) return 1 end function -- public API public procedure write(TBzLog me, integer level, sequence msg) -- SILENT, ERR, INFO, DEBUG, TRACE, VERBOSE if level <= get_log_level(me) then if level = SILENT then -- do nothing elsif level = ERR then log_error(me, msg) elsif level = INFO then log_info(me, msg) elsif level = DEBUG then log_debug(me, msg) elsif level = TRACE then log_trace(me, msg) elsif level = VERBOSE then log_verbose(me, msg) else log_error(me, "**INVALID LOGGER TYPE**") log_error(me, msg) end if end if end procedure -- Close the log file on exit procedure close_logger(TBzLog me) integer h = get_log_handle(me) if h != -1 then close(h) set_log_handle(me, -1) end if end procedure -- private function get_log_handle(TBzLog me) return eumem:ram_space[me][_log_handle] end function -- private procedure set_log_handle(TBzLog me, integer handle) eumem:ram_space[me][_log_handle] = handle end procedure -- private function get_log_level(TBzLog me) return eumem:ram_space[me][_log_level] end function -- private procedure set_log_level(TBzLog me, integer level) eumem:ram_space[me][_log_level] = level end procedure -- private logging function procedure log_line(TBzLog me, sequence level, sequence msg) sequence td = format(now(), "%Y-%m-%d %k:%M:%S") printf(get_log_handle(me), "[%s] [%s] %s\n", {td, level, msg}) end procedure -- private procedure log_info(TBzLog me, sequence msg) log_line(me, "info", msg) end procedure -- private procedure log_debug(TBzLog me, sequence msg) log_line(me, "debug", msg) end procedure -- private procedure log_trace(TBzLog me, sequence msg) log_line(me, "trace", msg) end procedure -- private procedure log_error(TBzLog me, sequence msg) log_line(me, "error", msg) end procedure -- private procedure log_verbose(TBzLog me, sequence msg) log_line(me, "verbose", msg) end procedure
And these are the integration style tests.
-- logger-manual-test.ex include lib/utils/bzlog.e function main() verbose() tracelog() debug() info() err() silent() puts(1, "Test Completed") return 1 end function procedure writeLogs(TBzLog o) bzlog:write(o, VERBOSE, "VERBOSE message") bzlog:write(o, TRACE, "TRACE message") bzlog:write(o, DEBUG, "DEBUG message") bzlog:write(o, INFO, "INFO message") bzlog:write(o, ERR, "ERR message\n") bzlog:write(o, SILENT, "SILENT message") end procedure procedure verbose() TBzLog olog = bzlog:new( VERBOSE, "logger_test.log" ) bzlog:write(olog, VERBOSE, "Starting logger in VERBOSE") writeLogs(olog) bzlog:free(olog) end procedure procedure tracelog() TBzLog olog = bzlog:new( TRACE, "logger_test.log" ) bzlog:write(olog, TRACE, "Starting logger in TRACE") writeLogs(olog) bzlog:free(olog) end procedure procedure debug() TBzLog olog = bzlog:new( DEBUG, "logger_test.log" ) bzlog:write(olog, DEBUG, "Starting logger in DEBUG") writeLogs(olog) bzlog:free(olog) end procedure procedure info() TBzLog olog = bzlog:new( INFO, "logger_test.log" ) bzlog:write(olog, INFO, "Starting logger in INFO") writeLogs(olog) bzlog:free(olog) end procedure procedure err() TBzLog olog = bzlog:new( ERR, "logger_test.log" ) bzlog:write(olog, ERR, "Starting logger in ERR") writeLogs(olog) bzlog:free(olog) end procedure procedure silent() TBzLog olog = bzlog:new( SILENT, "logger_test.log" ) bzlog:write(olog, SILENT, "Starting logger in SILENT") writeLogs(olog) bzlog:free(olog) end procedure main()
Not Categorized, Please Help
|